﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using System.Diagnostics;




using Microsoft.Xna.Framework.Graphics;

using Microsoft.Xna.Framework.Content;

using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Input;

using FXnaEngine;

using Microsoft.Xna.Framework;

using GameTime = Microsoft.Xna.Framework.GameTime;


namespace FXnaGraphics
{

    // System.Drawing and the XNA Framework both define Color and Rectangle
    // types. To avoid conflicts, we specify exactly which ones to use.
    using Color = System.Drawing.Color;
    using Rectangle = Microsoft.Xna.Framework.Rectangle;



    /// <summary>
    /// 下面的这个类，只能被应用于winform程序，
    /// 因为在wpf中，找寻句柄有点难，所以我们提供另一个控件以解决WPF调用的问题
    /// </summary>
    public  class FXnaControl : UserControl
    {


        #region 事件模型



        #endregion

        #region 构造函数

        public FXnaControl()
        {



            // Start the timer keeping track of total elapsed time
            totalTime.Start();

            // Hook the mouse down event for the mousedevice, so the control
            // will be selected when clicked on. This avoids problems with
            // mouse and keyboard camera commands affecting other controls on
            // the form
            this.MouseDown += new MouseEventHandler(FXnaControl_MouseDown);



           


        }




        void FXnaControl_MouseDown(object sender, MouseEventArgs e)
        {

            this.Select();


        }

   



        #endregion

        #region Fields


        // Simulate the GameTime ourselves because we don't have Game
        GameTime gameTime;

        // Elapsed and total time for the GameTime
        Stopwatch elapsedTime = new Stopwatch();
        Stopwatch totalTime = new Stopwatch();



        // Timer to keep track of refreshes
        Timer timer;




        // Camera, Keyboard, and Mouse will be handled here. They are
        // public and static so they can be used anywhere
        //public static FPSCamera Camera;
        //public static KeyboardDevice Keyboard;
        //public static MouseDevice Mouse;






        // However many GraphicsDeviceControl instances you have, they all share
        // the same underlying GraphicsDevice, managed by this helper service.
        GraphicsDeviceService graphicsDeviceService;

        //ServiceContainer services = new ServiceContainer();


        ContentManager contentManger;

        FGame game = new FGame();



        GameServiceContainer serviceContainer = new GameServiceContainer();

       

       

        #endregion


        #region Properties


        /// <summary>
        /// Gets a GraphicsDevice that can be used to draw onto this control.
        /// </summary>
        public GraphicsDevice GraphicsDevice
        {
            get { return graphicsDeviceService.GraphicsDevice; }
        }


        /// <summary>
        /// Gets an IServiceProvider containing our IGraphicsDeviceService.
        /// This can be used with components such as the ContentManager,
        /// which use this service to look up the GraphicsDevice.
        /// </summary>
        //public ServiceContainer Services
        //{
        //    get { return services; }
        //}


       


        public FGame Game
        {
            get { return game; }
            
        }





        #endregion


        #region 初始化


        /// <summary>
        /// Initializes the control.
        /// </summary>
        protected override void OnCreateControl()
        {
            // Don't initialize the graphics device if we are running in the designer.
            if (!DesignMode)
            {

                //对于winform必须加入这一句否则鼠标捕获会失败，ScrollWheelValue


                Mouse.WindowHandle = this.FindForm().Handle;



                graphicsDeviceService = GraphicsDeviceService.AddRef(Handle,
                                                                     ClientSize.Width,
                                                                     ClientSize.Height);

                // Register the service, so components like ContentManager can find it.

                //services.AddService<IGraphicsDeviceService>(graphicsDeviceService);


                //这里必须注册device服务，否则content找不到对应的device
                serviceContainer.AddService(typeof(IGraphicsDeviceService), graphicsDeviceService);



                contentManger = new ContentManager(serviceContainer);

               

                //下面的代码缺少动态更新的部分


                this.game.Location = this.Location;

                //this.game.Size = this.Size;

                //暂时以缓冲区代替



                this.game.InitEngine(this.graphicsDeviceService, contentManger, serviceContainer);



                // Give derived classes a chance to initialize themselves.
                Initialize();


               
            }

            base.OnCreateControl();
        }


      


        /// <summary>
        /// Disposes the control.
        /// </summary>
        protected override void Dispose(bool disposing)
        {
            if (graphicsDeviceService != null)
            {
                graphicsDeviceService.Release(disposing);
                graphicsDeviceService = null;
            }

            base.Dispose(disposing);
        }




        #endregion



        #region Paint




        /// <summary>
        /// Redraws the control in response to a WinForms paint message.
        /// </summary>
        protected override void OnPaint(PaintEventArgs e)
        {
            if (DesignMode)
            {

                return;
                
            }
            


            string beginDrawError = BeginDraw();

            if (string.IsNullOrEmpty(beginDrawError))
            {
                

                // Draw the control using the GraphicsDevice.
                Draw();
                EndDraw();
            }
            else
            {
                // If BeginDraw failed, show an error message using System.Drawing.
                PaintUsingSystemDrawing(e.Graphics, beginDrawError);
            }
        }


        /// <summary>
        /// Attempts to begin drawing the control. Returns an error message string
        /// if this was not possible, which can happen if the graphics device is
        /// lost, or if we are running inside the Form designer.
        /// </summary>
        string BeginDraw()
        {
            // If we have no graphics device, we must be running in the designer.
            if (graphicsDeviceService == null)
            {
                return Text + "\n\n" + GetType();
            }

            // Make sure the graphics device is big enough, and is not lost.
            string deviceResetError = HandleDeviceReset();

            if (!string.IsNullOrEmpty(deviceResetError))
            {
                return deviceResetError;
            }

            // Many GraphicsDeviceControl instances can be sharing the same
            // GraphicsDevice. The device backbuffer will be resized to fit the
            // largest of these controls. But what if we are currently drawing
            // a smaller control? To avoid unwanted stretching, we set the
            // viewport to only use the top left portion of the full backbuffer.
            Viewport viewport = new Viewport();

            viewport.X = 0;
            viewport.Y = 0;

            viewport.Width = ClientSize.Width;
            viewport.Height = ClientSize.Height;

            viewport.MinDepth = 0;
            viewport.MaxDepth = 1;

            GraphicsDevice.Viewport = viewport;

            return null;
        }


        /// <summary>
        /// Ends drawing the control. This is called after derived classes
        /// have finished their Draw method, and is responsible for presenting
        /// the finished image onto the screen, using the appropriate WinForms
        /// control handle to make sure it shows up in the right place.
        /// </summary>
        void EndDraw()
        {
            try
            {
                Rectangle sourceRectangle = new Rectangle(0, 0, ClientSize.Width,
                                                                ClientSize.Height);

                GraphicsDevice.Present(sourceRectangle, null, this.Handle);
            }
            catch
            {
                // Present might throw if the device became lost while we were
                // drawing. The lost device will be handled by the next BeginDraw,
                // so we just swallow the exception.
            }
        }


        /// <summary>
        /// Helper used by BeginDraw. This checks the graphics device status,
        /// making sure it is big enough for drawing the current control, and
        /// that the device is not lost. Returns an error string if the device
        /// could not be reset.
        /// </summary>
        string HandleDeviceReset()
        {
            bool deviceNeedsReset = false;

            switch (GraphicsDevice.GraphicsDeviceStatus)
            {
                case GraphicsDeviceStatus.Lost:
                    // If the graphics device is lost, we cannot use it at all.
                    return "Graphics device lost";

                case GraphicsDeviceStatus.NotReset:
                    // If device is in the not-reset state, we should try to reset it.
                    deviceNeedsReset = true;
                    break;

                default:
                    // If the device state is ok, check whether it is big enough.
                    PresentationParameters pp = GraphicsDevice.PresentationParameters;

                    deviceNeedsReset = (ClientSize.Width > pp.BackBufferWidth) ||
                                       (ClientSize.Height > pp.BackBufferHeight);
                    break;
            }

            // Do we need to reset the device?
            if (deviceNeedsReset)
            {
                try
                {
                    graphicsDeviceService.ResetDevice(ClientSize.Width,
                                                      ClientSize.Height);
                }
                catch (Exception e)
                {
                    return "Graphics device reset failed\n\n" + e;
                }
            }

            return null;
        }


        /// <summary>
        /// If we do not have a valid graphics device (for instance if the device
        /// is lost, or if we are running inside the Form designer), we must use
        /// regular System.Drawing method to display a status message.
        /// </summary>
        protected virtual void PaintUsingSystemDrawing(Graphics graphics, string text)
        {
            graphics.Clear(Color.CornflowerBlue);

            using (Brush brush = new SolidBrush(Color.Black))
            {
                using (StringFormat format = new StringFormat())
                {
                    format.Alignment = StringAlignment.Center;
                    format.LineAlignment = StringAlignment.Center;

                    graphics.DrawString(text, Font, brush, ClientRectangle, format);
                }
            }
        }


        /// <summary>
        /// Ignores WinForms paint-background messages. The default implementation
        /// would clear the control to the current background color, causing
        /// flickering when our OnPaint implementation then immediately draws some
        /// other color over the top using the XNA Framework GraphicsDevice.
        /// </summary>
        protected override void OnPaintBackground(PaintEventArgs pevent)
        {
        }


        #endregion



        #region 具体执行的绘制函数

        protected  void Initialize()
        {

            // Hook the idle event to constantly redraw our animation.
            //改变即时重回,以后通过参数的形式调节

           // Application.Idle += delegate { Invalidate(); };





          
            //初始化对应的资源

            this.game.Initialize();

            //加载对应的资源,必要的系统资源，如字体 等


            this.game.LoadContent();





            // Set up the frame update timer
            timer = new Timer();

            // Lock framerate to 40 so we can keep performance up
            timer.Interval = (int)((1f / 40f) * 1000);

            // Hook timer's tick so we can refresh the view on cue
            timer.Tick += new EventHandler(timer_Tick);
            timer.Start();





        }



        void timer_Tick(object sender, EventArgs e)
        {

            // Invalidate everything so the whole control refreshes
            this.Invalidate();

            // Force the view update
            this.Update();


        }


        protected  void Draw()
        {




            // Update GameTime and update the engine
            UpdateGameTime();

            this.game.Update(gameTime);

            // Update GameTime again and draw the scene
            UpdateGameTime();


            this.game.Draw(gameTime);



               


          

          




        




        }
       




        // Updates the GameTime object instead of relying on Game
        void UpdateGameTime()
        {
            // Recreate the GameTime with the current values
            gameTime = new GameTime(totalTime.Elapsed, elapsedTime.Elapsed);

            // Restart the elapsed timer that keeps track of time between frames
            elapsedTime.Reset();
            elapsedTime.Start();
        }



        #endregion



    }
}
